#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <debug.h>
#include "../ident.h"
#include "../opcodes.h"
#include "../compiler.h"
#include "../trees.h"
#include "../grammar.tab.h"

int debug_level = 7;

typedef struct lexer_output_s {
	int type;
	union YYSTYPE u;
	int lineno;
	int yywarn;
	int yyerror;
} lexer_output_t;

static lexer_output_t output[20];
static int total_count;
static int pass_count;
static int last_yywarns;
static int last_yyerrors;
static int extra_yywarns;
static int extra_yyerrors;

#include "token_str.c"

static void run_test(char *input, int token_count);

/* lexer_tests.c are generated automatically by lexer_tests.py */
#include "lexer_tests.c"

int main(int argc, char *argv[])
{
	init_identifiers();
	init_locals();

	total_count = 0;
	pass_count = 0;

	printf("################### Tests for Lexer ###################\n");

	run_testsuite();

	if (pass_count == total_count) {
		printf("# OK (%d tests)\n",
				total_count);
	} else {
		printf("# Tests run: %d, Failure: %d\n",
				total_count, total_count - pass_count);
	}
	printf("#######################################################\n");

	return 0;
}

static int compare_real(int i, float a, float b)
{
	if (a != b) {
		printf("# Test %d - Wrong value of token[%d].real:\n# Receive: %f\n# Expected: %f\n",
				total_count, i, a, b);
		return 1;
	}
	return 0;
}

static int compare_ihe(int i, ident_hash_elem_t *a, ident_hash_elem_t *b)
{
	if (strcmp(a->name, b->name)) {
		printf("# Test %d - Wrong name of token[%d].ihe:\n# Receive: %s\n# Expected: %s\n",
				total_count, i, a->name, b->name);
		return 1;
	}
	if (a->token != b->token) {
		printf("# Test %d - Wrong token of token[%d].ihe:\n# Receive: %d\n# Expected: %d\n",
				total_count, i, a->token, b->token);
		return 1;
	}
	if (a->sem_value != b->sem_value) {
		printf("# Test %d - Wrong sem_value of token[%d].ihe:\n# Receive: %d\n# Expected: %d\n",
				total_count, i, a->sem_value, b->sem_value);
		return 1;
	}
	return 0;
}

static int compare_str(int i, char *a, char *b)
{
	if (strcmp(a, b)) {
		printf("# Test %d - Wrong value of token[%d].string:\n# Receive: %s\n# Expected: %s\n",
				total_count, i, a, b);
		return 1;
	}
	return 0;
}

static int compare_num(int i, int a, int b)
{
	if (a != b) {
		printf("# Test %d - Wrong value of token[%d].number:\n# Receive: %d\n# Expected: %d\n",
				total_count, i, a, b);
		return 1;
	}
	return 0;
}

static char non_token[1];

static char *token2str(int num)
{
	if (num < token_str_base) {
		/* not a token, just a char like '+' */
		non_token[0] = num;
		return non_token;
	}
	return token_str[num - token_str_base];
}

static int check_token(int i, int token)
{
	/* check type */
	if (output[i].type == -1) {
		if (token != output[i].u.number) {
			printf("# Test %d - Wrong type of token[%d]:\n# Receive: %s\n# Expected: %s\n",
					total_count, i, token2str(token), token2str(output[i].u.number));
			return -1;
		}
	} else {
		if (token != output[i].type) {
			printf("# Test %d - Wrong type of token[%d]:\n# Receive: %s\n# Expected: %s\n",
					total_count, i, token2str(token), token2str(output[i].type));
			return -1;
		}

		/* check yylval */
		switch (output[i].type) {
			case L_REAL:
				if (compare_real(i, yylval.real, output[i].u.real)) {
					return -1;
				}
				break;

			case L_DEFINED_NAME:
				if (compare_ihe(i, yylval.ihe, output[i].u.ihe)) {
					return -1;
				}
				break;

			case L_STRING:
			case L_IDENTIFIER:
				if (compare_str(i, yylval.string, output[i].u.string)) {
					return -1;
				}
				break;

			default:
				/* number */
				if (compare_num(i, yylval.number, output[i].u.number)) {
					return -1;
				}
				break;
		}
	}

	/* check lineno */
	if (output[i].lineno != -1 && output[i].lineno != current_line) {
		printf("# Test %d - Wrong lineno of token[%d]:\n# Receive: %d\n# Expected: %d\n",
				total_count, i, current_line, output[i].lineno);
		return -1;
	}

	/* check yywarn */
	if (output[i].yywarn != num_parse_warning - last_yywarns) {
		printf("# Test %d - Wrong number of yywarn for token[%d]:\n# Receive: %d\n# Expected: %d\n",
				total_count, i, num_parse_warning - last_yywarns, output[i].yywarn);
		return -1;
	}

	/* check yyerror */
	if (output[i].yyerror != num_parse_error - last_yyerrors) {
		printf("# Test %d - Wrong number of yyerror for token[%d]:\n# Receive: %d\n# Expected: %d\n",
				total_count, i, num_parse_error - last_yyerrors, output[i].yyerror);
		return -1;
	}

	return 0;
}

static void run_test(char *input, int token_count)
{
	int token, i = 0;

	total_count++;

	num_parse_warning = 0;
	num_parse_error = 0;

	current_line = 1;

	yy_switch_to_buffer(yy_scan_string(input));

	/* no token */
	if (token_count == 0) {
		if (yylex()) {
			printf("# Test %d - No token expected\n", total_count);
			printf("# Input:\n%s\n", input);
			return;
		}
		if (num_parse_warning != extra_yywarns) {
			printf("# Test %d - Wrong number of yywarn:\n# Receive: %d\n# Expected: %d\n",
					total_count, num_parse_warning, extra_yywarns);
			return;
		}
		if (num_parse_error != extra_yyerrors) {
			printf("# Test %d - Wrong number of yyerror:\n# Receive: %d\n# Expected: %d\n",
					total_count, num_parse_error, extra_yyerrors);
			return;
		}
		pass_count++;
		return;
	}

	for (;;) {
		last_yywarns = num_parse_warning;
		last_yyerrors = num_parse_error;

		token = yylex();

		if (!token) {
			if (i < token_count) {
				printf("# Test %d - Wrong number of tokens:\n# Receive: %d\n# Expected: %d\n",
						total_count, i, token_count);
				printf("# Input:\n%s\n", input);
				return;
			}
			break;
		} else if (i >= token_count) {
			printf("# Test %d - Wrong number of tokens:\n# Receive: >= %d\n# Expected: %d\n",
					total_count, i, token_count);
			printf("# Input:\n%s\n", input);
			return;
		}

		if(check_token(i, token)) {
			printf("# Input:\n%s\n", input);
			return;
		}

		i++;
	}

	pass_count++;
}
